#include <cassert>
#include <iostream>

#include "GameObject.hpp"

#include "../renderer/Renderer.hpp"
#include "../collider/Collider.hpp"
#include "../behaviour/Behaviour.hpp"

#include "../collider/SphereCollider.hpp"

#include "../core/Mesh.hpp"
#include "../core/Texture.hpp"

GameObject::GameObject(std::string aName, glm::vec3 aPosition) : name(aName), transform(glm::translate(aPosition)), collider(NULL), behaviour(NULL), mesh(NULL), colorMap(NULL) {
    parent = NULL;
}

GameObject::~GameObject() {
    //dtor
}

glm::mat4 GameObject::getTransform() {
    return transform;
}

void GameObject::setTransform(glm::mat4 transform) {
    this->transform = transform;
}

void GameObject::translate(glm::vec3 translation) {
    transform = glm::translate(transform, translation);
}

void GameObject::rotate(float angle, glm::vec3 axis) {
    transform = glm::rotate(transform, angle, axis);
}

const std::string GameObject::getName() {
    return name;
}

void GameObject::setName(std::string name) {
    this->name = name;
}

glm::vec3 GameObject::getLocation() {
    return glm::vec3(transform[3][0], transform[3][1], transform[3][2]);
}

void GameObject::addLocation(glm::vec3 location) {
    transform[3][0] += location.x;
    transform[3][1] += location.y;
    transform[3][2] += location.z;
}

void GameObject::setLocation(glm::vec3 location) {
    transform[3][0] = location.x;
    transform[3][1] = location.y;
    transform[3][2] = location.z;
}

bool GameObject::hasCollider() {
    return collider != NULL;
}

Behaviour* GameObject::getBehaviour() {
    return behaviour;
}

void GameObject::setBehaviour(Behaviour* aBehaviour) {
    assert(aBehaviour != NULL);
    behaviour = aBehaviour;
}

Collider* GameObject::getCollider() {
    return collider;
}

void GameObject::setCollider(Collider* aCollider) {
    assert(aCollider != NULL);
    collider = aCollider;
}

Mesh* GameObject::getMesh() {
    return mesh;
}

void GameObject::setMesh(Mesh* aMesh) {
    assert(aMesh != NULL);
    mesh = aMesh;
}

Texture* GameObject::getColorMap() {
    return colorMap;
}

void GameObject::setColorMap(Texture* aColorMap) {
    assert(aColorMap != NULL);
    assert(aColorMap->getId() > 0);
    colorMap = aColorMap;
}

bool GameObject::collides(GameObject* otherGameObject) {
    SphereCollider* collider = (SphereCollider*) getCollider();
    SphereCollider* other = (SphereCollider*) otherGameObject->getCollider();
    bool result =
        getLocation().x+collider->getRadius() > otherGameObject->getLocation().x-other->getRadius() && getLocation().x-collider->getRadius() < otherGameObject->getLocation().x+other->getRadius() &&
        getLocation().y+collider->getRadius() > otherGameObject->getLocation().y-other->getRadius() && getLocation().y-collider->getRadius() < otherGameObject->getLocation().y+other->getRadius() &&
        getLocation().z+collider->getRadius() > otherGameObject->getLocation().z-other->getRadius() && getLocation().z-collider->getRadius() < otherGameObject->getLocation().z+other->getRadius();
    //std:: cout << "Check " << name << " - " << other->name << " : " << result << std::endl;
    return result;

    //assert(collider != NULL);
    //assert(otherGameObject != NULL);
    //assert(otherGameObject->collider != NULL);
    //    return collider->collides(otherGameObject->collider);
    return false;
}

void GameObject::onCollision(GameObject* otherGameObject) {
    if(behaviour) {
        behaviour->onCollision(otherGameObject);
    }
}

void GameObject::add(GameObject* child) {
    assert(child != NULL);
    children.push_back(child);
    child->parent = this;
}

void GameObject::accept(Visitor* visitor) {
    visitor->visit(this);
}

GameObject* GameObject::getParent() {
    return parent;
}

std::vector<GameObject*>* GameObject::getChildren() {
    return &children;
}

/*
//Recursive method to collect all objects
std::vector<GameObject*>* GameObject::getAllChildren(std::vector<GameObject*>* childrenVec) {
    std::vector<GameObject*>* allChildren = childrenVec;

    for(std::vector<GameObject*>::iterator it = children.begin(); it != children.end(); ++it) {
        allChildren->push_back((*it)); //collect all direct children.
        (*it)->getAllChildren(allChildren); //pass through the collected children in a list and continue to do untill vector is empty
    }

    return allChildren;
}
*/

void GameObject::deleteObject(GameObject* child) {
    for(std::vector<GameObject*>::iterator it = children.begin(); it != children.end(); ++it) {
        if((*(it))== child) {
            //children.erase(it);
            //std::cout<<"Deleted: "<<(*it)->getName()<<std::endl;
            //delete (*it);
            return;
        }
    }
}

glm::vec3 GameObject::getAbsolutePosition() {
    if(parent != NULL) {
        return parent->getAbsolutePosition() + getLocation();
    }

    return getLocation();
}
// private functions
